
#include "PCH.h"
#include "Scene.h"
#include "Camera.h"
#include "ObjReader.h"
#include "MetaSphere.h"
#include "Timer.h"

#include "constants.h"

#include "Globals.h"

bool CScene::TestRay(const CRay &Ray)
{
	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		if( Obj->HitDistance(Ray)<Infinity)
		{
			return true;
			break;
		}
	}
	return false;
}

void CScene::RenderRay(CRay Ray, CColor &ResultColor)
{
	Timer Chrono;
	CRay Ray2=Ray;
	CMaterial Mat;
	double MinimumDistance = Infinity;
	double DistanceToSurface = 0;
	CRenderableObject* ClosestObject = NULL;
	ResultColor.r=0.0;
	ResultColor.g=0.0;
	ResultColor.b=0.0;
	CColor colorD;
	CColor colorR;
	CColor colorRefr;
	CRay NormalRay;
	Chrono.reset();
	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		double Distance = Obj->HitDistance(Ray2);
		if (Distance>eps && Distance < MinimumDistance)
		{
			MinimumDistance = Distance;
			ClosestObject = Obj;
		}
	}
	Globals::Instance().CollisionTime+=Chrono.milliseconds();

/*	if(ClosestObject!=NULL)
	{
		CVector ImpactPoint = Ray2.Origin+Ray2.Direction*MinimumDistance;
		CRay Normal;
		ClosestObject->GetNormal(ImpactPoint,Normal);
		if(Normal.Direction.DotProduct(Ray2.Direction)>0)
			Ray2.DepthInObject--;
		else
			Ray2.DepthInObject++;
	}
	while (ClosestObject!=NULL && Ray2.DepthInObject<=0)
	{
		DistanceToSurface+=MinimumDistance;
		CVector ImpactPoint = Ray2.Origin+Ray2.Direction*MinimumDistance;
		Ray2.Origin = ImpactPoint;
		MinimumDistance = Infinity;
		ClosestObject = NULL;
		for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
		{
			CRenderableObject* Obj = *it;
			double Distance = Obj->HitDistance(Ray2);
			if (Distance>eps && Distance < MinimumDistance)
			{
				MinimumDistance = Distance;
				ClosestObject = Obj;
			}
		}
		if(ClosestObject!=NULL)
		{
			CVector ImpactPoint = Ray2.Origin+Ray2.Direction*MinimumDistance;
			CRay Normal;
			ClosestObject->GetNormal(ImpactPoint,Normal);
			if(Normal.Direction.DotProduct(Ray2.Direction)>0)
				Ray2.DepthInObject--;
			else
				Ray2.DepthInObject++;
		}
	}*/
	if(ClosestObject!=NULL /*&& Ray2.DepthInObject>0*/)
	{
		Chrono.reset();

		//		if(DistanceToSurface==0)
			DistanceToSurface+=MinimumDistance;
		CColor DiffuseColor,SpecularColor;
		CVector ImpactPoint = CVector(Ray.Origin.x+DistanceToSurface*Ray.Direction.x,Ray.Origin.y+DistanceToSurface*Ray.Direction.y,Ray.Origin.z+DistanceToSurface*Ray.Direction.z);
		ClosestObject->GetNormal(ImpactPoint,NormalRay);
		ClosestObject->GetMaterial(ImpactPoint,Mat);
		//ImpactPoint+=NormalRay.Direction*eps;
		for(std::vector<CLight*>::iterator it = Lights.begin(); it != Lights.end(); ++it)
		{
			CLight* Light = *it;
			if (SeeLight(ImpactPoint,Light))
			{
				CVector ToLightVector = CVector(Light->Position.x-ImpactPoint.x,Light->Position.y-ImpactPoint.y,Light->Position.z-ImpactPoint.z);
				ToLightVector.Normalize();
				double cosAngle = NormalRay.Direction.DotProduct(ToLightVector);
				if (cosAngle>0)
				{
					DiffuseColor=Mat.color*cosAngle*Mat.diffuse;

					CRay ReflectedLight;
					ReflectedLight.Origin = ImpactPoint;
					double dp2 = 2*ToLightVector.DotProduct(NormalRay.Direction);
					ReflectedLight.Direction = ToLightVector-(NormalRay.Direction*dp2);

					double dot = ReflectedLight.Direction.DotProduct(Ray.Direction);
					if (dot > 0)
					{
						SpecularColor.r = pow( dot, Mat.specularExponent  ) * Mat.specular;
						SpecularColor.g=SpecularColor.r;
						SpecularColor.b=SpecularColor.r;
					} 
					SpecularColor*=Light->Intensity;
				}
				else
				{
					DiffuseColor.r=0.0;
					DiffuseColor.g=0.0;
					DiffuseColor.b=0.0;
					SpecularColor.r=0.0;
					SpecularColor.g=0.0;
					SpecularColor.b=0.0;
				}
				colorD=colorD+(DiffuseColor+SpecularColor)*Light->Intensity;
			}
		}
		if(ClosestObject->Selected)
			colorD=CColor(1.0,0.5,1.0);
		Globals::Instance().DiffuseTime+=Chrono.milliseconds();

		colorD+=Ambiant*Mat.color;
/*		double AOComponent = pow(
			1.0-exp(-1.0*AO(NormalRay,ClosestObject)*AO_Exposure)
			,AO_POW)*AO_STRENGHT;
		colorD+=Mat.color*Ambiant*AOComponent;
*/
#if RADIOSITY
		// RADIOSITY - START
			{
				CColor RadColor;
				RadColor.r =0.0;
				RadColor.g =0.0;
				RadColor.b =0.0;
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				LocalRay.Direction = NormalRay.Direction;
				CVector RN1 = CVector(LocalRay.Direction.z,LocalRay.Direction.y,-LocalRay.Direction.x);
				CVector RN2 = LocalRay.Direction.CrossProduct(RN1);
				LocalRay.intensity = Ray.intensity;
				LocalRay.BounceThreshold=Ray.BounceThreshold-1;
				if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
				{
					for(int scat=0;scat<int((DIFFUSE_REFLECTION_COUNT/(1.0f*(MaxBounce-LocalRay.BounceThreshold+1)*(1+MaxBounce-LocalRay.BounceThreshold))));++scat)
					{
						double xoffs, yoffs;
						do 
						{
							xoffs = 10.0f*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0);
							yoffs = 10.0f*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0);
						} while((xoffs*xoffs+yoffs*yoffs)>(10.0f*10.0f));
						CRay R = LocalRay;
						R.Direction = R.Direction + RN1 * xoffs+RN2 * yoffs;
						R.Direction.Normalize();
						CColor colorR2;
						RenderRay(R,colorR2);
						RadColor+=colorR2;
					}
					RadColor *= 1.0f/(DIFFUSE_REFLECTION_COUNT/(1.0f*(MaxBounce-LocalRay.BounceThreshold+1.0f)*(MaxBounce-LocalRay.BounceThreshold)));
					RadColor *= Mat.color;

				}				
				colorD += RadColor;
			}
		// RADIOSITY - END
#endif


#if 1
		Chrono.reset();
		// REFLECTION - START
		if(Mat.reflection.r+Mat.reflection.g+Mat.reflection.b>0.0)
		{
			if(Mat.reflectionScatter	==0.0f)
			{
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				double dp2 = 2*Ray.Direction.DotProduct(NormalRay.Direction);
				LocalRay.Direction = Ray.Direction-(NormalRay.Direction*dp2);
				LocalRay.intensity = (Mat.reflection.r+Mat.reflection.g+Mat.reflection.b)*Ray.intensity;
				LocalRay.BounceThreshold=Ray.BounceThreshold-1;
				if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
				{
					Globals::Instance().ReflectionTime+=Chrono.milliseconds();
					RenderRay(LocalRay,colorR);
					Chrono.reset();
					colorR *=Mat.reflection;
				}
				else
				{
					colorR.r =0.0;
					colorR.g =0.0;
					colorR.b =0.0;
				}
			}
			else
			{
				colorR.r =0.0;
				colorR.g =0.0;
				colorR.b =0.0;
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				double dp2 = 2*Ray.Direction.DotProduct(NormalRay.Direction);
				LocalRay.Direction = Ray.Direction-(NormalRay.Direction*dp2);
				CVector RN1 = CVector(LocalRay.Direction.z,LocalRay.Direction.y,-LocalRay.Direction.x);
				CVector RN2 = LocalRay.Direction.CrossProduct(RN1);
				LocalRay.intensity = (Mat.reflection.r+Mat.reflection.g+Mat.reflection.b)*Ray.intensity;
				LocalRay.BounceThreshold=Ray.BounceThreshold-1;
				if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
				{
					for(int scat=0;scat<int((DIFFUSE_REFLECTION_COUNT/(1.0f*(MaxBounce-LocalRay.BounceThreshold+1)*(1+MaxBounce-LocalRay.BounceThreshold))));++scat)
					{
						double xoffs, yoffs;
						do 
						{
							xoffs = Mat.reflectionScatter*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0);
							yoffs = Mat.reflectionScatter*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0)*(rand()*2.0f/RAND_MAX-1.0);
						} while((xoffs*xoffs+yoffs*yoffs)>(Mat.reflectionScatter*Mat.reflectionScatter));
						CRay R = LocalRay;
						R.Direction = R.Direction + RN1 * xoffs+RN2 * yoffs;
						R.Direction.Normalize();
						CColor colorR2;
						Globals::Instance().ReflectionTime+=Chrono.milliseconds();
						RenderRay(R,colorR2);
						Chrono.reset();
						colorR+=colorR2;
					}
					colorR *= 1.0/(DIFFUSE_REFLECTION_COUNT/(1.0f*(MaxBounce-LocalRay.BounceThreshold+1)*(1+MaxBounce-LocalRay.BounceThreshold)));
					colorR *= Mat.reflection;

/*					if(MaxBounce-LocalRay.BounceThreshold<=1)
					{
						for(int layer=1;layer <=32;++layer)
						{
							double radius = Mat.reflectionScatter * layer * layer / 1024.0;
							double offset = layer * PI / 180.0;
							double xoffs, yoffs;
							int MaxPerLayer = DIFFUSE_REFLECTION_COUNT / 32;
							for(int rotating = 0; rotating<MaxPerLayer;++rotating)
							{
								xoffs = radius *sin(rotating*1.0/MaxPerLayer*2*PI);
								yoffs = radius *cos(rotating*1.0/MaxPerLayer*2*PI); 
								CRay R = LocalRay;
								R.Direction = R.Direction + RN1 * xoffs+RN2 * yoffs;
								R.Direction.Normalize();
								CColor colorR2;
								RenderRay(R,colorR2);
								colorR+=colorR2;
							}
							CColor colorR2;
							RenderRay(LocalRay,colorR2);
							colorR+=colorR2;
							colorR *= 1.0/(DIFFUSE_REFLECTION_COUNT+1);
							colorR *= Mat.reflection;
						}
					}
					else
					{
						CColor colorR2;
						RenderRay(LocalRay,colorR2);
						colorR+=colorR2;
						colorR *= Mat.reflection;
					}
*/				}					
			}
		}
		else
		{
			colorR.r =0.0;
			colorR.g =0.0;
			colorR.b =0.0;
		}

		// REFLECTION - END
		Globals::Instance().ReflectionTime+=Chrono.milliseconds();

#endif

#if 1
		Chrono.reset();
	// REFRACTION - START
		if(Mat.transparency>0.0)
		{
			colorRefr.r =0.0;
			colorRefr.g =0.0;
			colorRefr.b =0.0;
			if(Mat.refractionIndex.r!=Mat.refractionIndex.g || Mat.refractionIndex.r!=Mat.refractionIndex.b)
			{
			{//Computing for Red
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				double nDiv = Mat.refractionIndex.r;
				CVector incedent = Ray.Direction;
				double cosI = -NormalRay.Direction.DotProduct(incedent);
				if(cosI<0)
					nDiv = 1/nDiv ;
				double sinI = sqrt(1.0f - cosI*cosI);
				double sin2R = sinI/nDiv * sinI/nDiv;
				if (sin2R < 1.0f) {
					CVector intRefrac;
					double coefficient = (sqrt(1.0f-sin2R)-nDiv*cosI);
					intRefrac = (incedent*nDiv) - (NormalRay.Direction*coefficient);
					intRefrac.Normalize();
					//new ray based on intersect point and calculated refraction
					LocalRay.Direction = intRefrac;

					LocalRay.intensity = Mat.transparency*Ray.intensity;
					LocalRay.BounceThreshold=Ray.BounceThreshold-1;
					if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
					{
						CColor colorRefrComponent;
						RenderRay(LocalRay,colorRefrComponent);
						colorRefrComponent *=Mat.color*Mat.transparency;
						colorRefrComponent *=CColor(1.0,0,0);
						colorRefr+=colorRefrComponent;
					}
				}
			}
			{//Computing for Green
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				double nDiv = Mat.refractionIndex.g;
				CVector incedent = Ray.Direction;
				double cosI = -NormalRay.Direction.DotProduct(incedent);
				if(cosI<0)
					nDiv = 1/nDiv ;
				double sinI = sqrt(1.0f - cosI*cosI);
				double sin2R = sinI/nDiv * sinI/nDiv;
				if (sin2R < 1.0f) {
					CVector intRefrac;
					double coefficient = (sqrt(1.0f-sin2R)-nDiv*cosI);
					intRefrac = (incedent*nDiv) - (NormalRay.Direction*coefficient);
					intRefrac.Normalize();
					//new ray based on intersect point and calculated refraction
					LocalRay.Direction = intRefrac;

					LocalRay.intensity = Mat.transparency*Ray.intensity;
					LocalRay.BounceThreshold=Ray.BounceThreshold-1;
					if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
					{
						CColor colorRefrComponent;
						RenderRay(LocalRay,colorRefrComponent);
						colorRefrComponent *=Mat.color*Mat.transparency;
						colorRefrComponent *=CColor(0,1.0,0);
						colorRefr+=colorRefrComponent;
					}
				}
			}
			{//Computing for Blue
				CRay LocalRay;
				LocalRay.Origin = ImpactPoint;
				double nDiv = Mat.refractionIndex.b;
				CVector incedent = Ray.Direction;
				double cosI = -NormalRay.Direction.DotProduct(incedent);
				if(cosI<0)
					nDiv = 1/nDiv ;
				double sinI = sqrt(1.0f - cosI*cosI);
				double sin2R = sinI/nDiv * sinI/nDiv;
				if (sin2R < 1.0f) {
					CVector intRefrac;
					double coefficient = (sqrt(1.0f-sin2R)-nDiv*cosI);
					intRefrac = (incedent*nDiv) - (NormalRay.Direction*coefficient);
					intRefrac.Normalize();
					//new ray based on intersect point and calculated refraction
					LocalRay.Direction = intRefrac;

					LocalRay.intensity = Mat.transparency*Ray.intensity;
					LocalRay.BounceThreshold=Ray.BounceThreshold-1;
					if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
					{
						CColor colorRefrComponent;
						RenderRay(LocalRay,colorRefrComponent);
						colorRefrComponent *=Mat.color*Mat.transparency;
						colorRefrComponent *=CColor(0,0,1.0);
						colorRefr+=colorRefrComponent;
					}
				}
			}
			}
			else
			{
				{//Computing for all Channels
					CRay LocalRay;
					LocalRay.Origin = ImpactPoint;
					double nDiv = Mat.refractionIndex.b;
					CVector incedent = Ray.Direction;
					double cosI = -NormalRay.Direction.DotProduct(incedent);
					if(cosI<0)
						nDiv = 1/nDiv ;
					double sinI = sqrt(1.0f - cosI*cosI);
					double sin2R = sinI/nDiv * sinI/nDiv;
					if (sin2R < 1.0f) {
						CVector intRefrac;
						double coefficient = (sqrt(1.0f-sin2R)-nDiv*cosI);
						intRefrac = (incedent*nDiv) - (NormalRay.Direction*coefficient);
						intRefrac.Normalize();
						//new ray based on intersect point and calculated refraction
						LocalRay.Direction = intRefrac;

						LocalRay.intensity = Mat.transparency*Ray.intensity;
						LocalRay.BounceThreshold=Ray.BounceThreshold-1;
						if ((LocalRay.BounceThreshold>0)&&(LocalRay.intensity > IntensityThreshold))
						{
							CColor colorRefrComponent;
							RenderRay(LocalRay,colorRefrComponent);
							colorRefrComponent *=Mat.color*Mat.transparency;
							colorRefr+=colorRefrComponent;
						}
					}
				}

			}
		}
		else
		{
			colorRefr.r =0.0;
			colorRefr.g =0.0;
			colorRefr.b =0.0;
		}
		Globals::Instance().RefractionTime+=Chrono.milliseconds();

		// REFRACTION - END
#endif
		CColor CausticLightColor;

#if Caustics
		RenderReflectionCaustics(NormalRay,CausticLightColor);
#endif
		ResultColor=(colorD+colorR+colorRefr+CausticLightColor*Mat.diffuse/10.0);

	}
}

bool CScene::SeeLight(const CVector &ImpactPoint,const CLight *Light)
{
	double MinimumDistance = ImpactPoint.Distance(Light->Position);
// 		sqrt((ImpactPoint.x-Light->Position.x)*(ImpactPoint.x-Light->Position.x)
// 		+(ImpactPoint.y-Light->Position.y)*(ImpactPoint.y-Light->Position.y)
// 		+(ImpactPoint.z-Light->Position.z)*(ImpactPoint.z-Light->Position.z));
	if(Light->Type == CLight::LightType_Spot)
	{
		CVector DirectionToPoint = ImpactPoint-Light->Position;
		DirectionToPoint.Normalize();
		if(DirectionToPoint.DotProduct(Light->Direction)<cos(Light->ConeAngle/360.0f*PI))
			return false;
	}
	CRay LightRay;
	LightRay.Origin = /*Light->Position;*/ImpactPoint;
	LightRay.Direction = Light->Position-ImpactPoint/*ImpactPoint-Light->Position*/;
	LightRay.Direction.Normalize();
	double MinimumDistanceLight = MinimumDistance ;

#if BOOLEANOBJECTS_SUPPORT
	CRenderableObject* ClosestObject=NULL;

	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		double Distance = Obj->HitDistance(LightRay);
		if ((Distance < MinimumDistance/*-eps*/) && (Distance > eps))
		{
			MinimumDistance = Distance;
			ClosestObject = Obj;
		}
	}
	if(ClosestObject!=NULL)
	{
		CVector ImpactPoint = LightRay.Origin+LightRay.Direction*MinimumDistance;
		CRay Normal;
		ClosestObject->GetNormal(ImpactPoint,Normal);
		if(Normal.Direction.DotProduct(LightRay.Direction)>=0)
			LightRay.DepthInObject--;
		else
			LightRay.DepthInObject++;
	}
	while (ClosestObject!=NULL && LightRay.DepthInObject<=0 && MinimumDistanceLight>0)
	{
		MinimumDistance+=eps;
		CVector ImpactPoint2 = CVector(LightRay.Origin.x+MinimumDistance*LightRay.Direction.x,LightRay.Origin.y+MinimumDistance*LightRay.Direction.y,LightRay.Origin.z+MinimumDistance*LightRay.Direction.z);
		LightRay.Origin = ImpactPoint2;
		MinimumDistance = MinimumDistanceLight-MinimumDistance;
		MinimumDistanceLight = MinimumDistance; 
		ClosestObject = NULL;
		for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
		{
			CRenderableObject* Obj = *it;
			double Distance = Obj->HitDistance(LightRay);
			if ((Distance < MinimumDistance/*-eps*/) && (Distance > eps))
			{
				MinimumDistance = Distance;
				ClosestObject = Obj;
			}
		}
		if(ClosestObject!=NULL)
		{
			CVector ImpactPoint = CVector(LightRay.Origin.x+MinimumDistance*LightRay.Direction.x,LightRay.Origin.y+MinimumDistance*LightRay.Direction.y,LightRay.Origin.z+MinimumDistance*LightRay.Direction.z);
			CRay Normal;
			ClosestObject->GetNormal(ImpactPoint,Normal);
			if(Normal.Direction.DotProduct(LightRay.Direction)>=0)
				LightRay.DepthInObject--;
			else
				LightRay.DepthInObject++;
		}

	}
	MinimumDistance+=eps;
	//MinimumDistance = MinimumDistanceLight-MinimumDistance;
	//MinimumDistanceLight = MinimumDistance; 

	if((MinimumDistanceLight-MinimumDistance)<eps)
		return true;
	else
		return false;
	
#else
	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		double Distance = Obj->HitDistance(LightRay);
		if ((Distance < MinimumDistance-eps) && (Distance > eps))
		{
			return false;
		}
	}
	return true;
#endif
}

void CScene::RenderReflectionCaustics(const CRay &SourceNormalRay, CColor&ResultColor)
{
	CVector SourcePoint=SourceNormalRay.Origin;
	ResultColor.r=0;
	ResultColor.g=0;
	ResultColor.b=0;
	for(std::vector<CRenderableObject*>::iterator it = ReflectionCausticsEffectors.begin(); it != ReflectionCausticsEffectors.end(); ++it)
	{
		CRenderableObject* CausticEffector = *it;
		std::vector<CRay> CausticRays;
		CausticEffector->CausticRayLister(SourcePoint, CausticRays);

		for(std::vector<CRay>::iterator RayIterator = CausticRays.begin(); RayIterator != CausticRays.end(); ++RayIterator)
		{
			CRay CausticRay = *RayIterator;
			double CausticEffectorDistance = CausticEffector->HitDistance(CausticRay);

			CRenderableObject* ClosestObstacle=NULL;
			double ClosestObstacleDistance=CausticEffectorDistance;
			CVector ImpactPoint;
			ImpactPoint=CausticRay.Origin+CausticRay.Direction*CausticEffectorDistance;
			CRay NormalRay;
			CausticEffector->GetNormal(ImpactPoint,NormalRay);
			for(std::vector<CRenderableObject*>::iterator PotentialObstacles = Objects.begin(); PotentialObstacles != Objects.end(); ++PotentialObstacles)
			{
				CRenderableObject* PotentialObstacle = *PotentialObstacles;
				if(PotentialObstacle!=CausticEffector)
				{
					double distance2 = PotentialObstacle->HitDistance(CausticRay);
					if(distance2<eps)
						continue;
					if (CausticEffectorDistance >distance2)
					{
						ClosestObstacle=PotentialObstacle;
						break;
					}
				}
			}
			if(ClosestObstacle==NULL)
			{
				CRay LightTestRay;
				LightTestRay.Origin = ImpactPoint;
				double dp2 = 2*CausticRay.Direction.DotProduct(NormalRay.Direction);
				LightTestRay.Direction = CausticRay.Direction-(NormalRay.Direction*dp2);

				for(std::vector<CLight*>::iterator LightIterator = Lights.begin(); LightIterator != Lights.end(); ++LightIterator)
				{

					CLight* Light = *LightIterator;
					CSphereObject FakeLightBall;
					FakeLightBall.Position=Light->Position;
					FakeLightBall.Radius=5000;
					if(FakeLightBall.HitDistance(LightTestRay)!=Infinity)
					{
						double distance=Light->Position.Distance(ImpactPoint);
						for(std::vector<CRenderableObject*>::iterator PotentialObstacles = Objects.begin(); PotentialObstacles != Objects.end(); ++PotentialObstacles)
						{
							CRenderableObject* PotentialObstacle = *PotentialObstacles;
							double distance2 = PotentialObstacle->HitDistance(LightTestRay);
							if(distance2<eps)
								continue;
							if (distance>distance2)
							{
								ClosestObstacle=PotentialObstacle;
								break;
							}
						}
						if(ClosestObstacle==NULL)
						{
							// We have reflected light!!!!
							// 							ResultColor.r +=(1.0/CausticRays.size())*CausticEffector->Material.reflection*Light->Intensity.r/**CausticRay.Direction.DotProduct(SourceNormalRay.Direction)*/;
							// 							ResultColor.g +=(1.0/CausticRays.size())*CausticEffector->Material.reflection*Light->Intensity.g/**CausticRay.Direction.DotProduct(SourceNormalRay.Direction)*/;
							// 							ResultColor.b +=(1.0/CausticRays.size())*CausticEffector->Material.reflection*Light->Intensity.b/**CausticRay.Direction.DotProduct(SourceNormalRay.Direction)*/;
							
							//DEBUG
 							ResultColor.r =1.0;
 							ResultColor.g =1.0;
 							ResultColor.b =1.0;
						}
					}
				}
			}
		}
	}
}


void CScene::SetupScene(int FrameNumber)
{
/*	double offsetX;
	double offsetY;
	double offsetZ;

	double BaseOffsetX=0;
	double BaseOffsetY=-250;
	double BaseOffsetZ=250;

	for(int k=0;k<1;++k)
	{

	BaseOffsetX=0;
	BaseOffsetY=250-(sin((70+32.f/60)*PI/180))*cos(PI/6)*100*k;
	BaseOffsetZ=0+(1-cos((70+32.f/60)*PI/180))*cos(PI/6)*100*k;
	for(int i=0;i<(k+1);++i)
	{
		offsetX=BaseOffsetX-50*i;
		offsetY=BaseOffsetY;
		offsetZ=BaseOffsetZ-cos(PI/6)*i*100;
		for(int j=0;j<(i+1);++j)
		{
			object1= new CSphereObject;
		 	object1->Position = CVector(offsetX,offsetY,offsetZ);
 			object1->Radius = 50.0;
 			object1->Material = Mirror;
			object1->NegativeObject= false;
			Objects.push_back(object1);
			offsetX+=100;
		}
	}
	}


*/


	//object1= new CSphereObject;
 //	object1->Position = CVector(0.0,-100.0,-100.0);
 //	object1->Radius = 30.0;
 //	object1->Material = RedMatte;
	//object1->NegativeObject= false;
 //
 //	Objects.push_back(object1);

	//object1= new CSphereObject;
 //	object1->Position = CVector(0.0,0.0,0.0);
 //	object1->Radius = 100.0;
 //	object1->Material = BlueMatte;
	//object1->NegativeObject= false;
 //
 //	Objects.push_back(object1);

//	CTriangleObject * object2;

	
	
/*	object2 = new CTriangleObject;
	object2->Point1 = CVector(0.0,0.0,1000.0);
	object2->Point2 = CVector(1000.0,1000.0,0.0);
	object2->Point3 = CVector(-1000.0,1000.0,0.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(1000.0,-1000.0,500.0);
	object2->Point2 = CVector(1000.0,1000.0,0.0);
	object2->Point3 = CVector(0.0,0.0,1000.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(3000.0,1000.0,1000.0);
	object2->Point3 = CVector(1000.0,-1000.0,2000.0);
	object2->Point2 = CVector(1000.0,-1000.0,500.0);
	object2->Material = RedShine;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(2000.0,1000.0,2000.0);
	object2->Point3 = CVector(1000.0,0.0,0.0);
	object2->Point2 = CVector(1000.0,1000.0,0.0);
	object2->Material = BlueShine;

	Objects.push_back(object2);
*/

	/*
	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,-1000.0,-1000.0);
	object2->Point2 = CVector(1000.0,-1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,1000.0,-1000.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,1000.0,-1000.0);
	object2->Point2 = CVector(-1000.0,-1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,1000.0,-1000.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(1000.0,-1000.0,-1000.0);
	object2->Point2 = CVector(-1000.0,-1000.0,-1000.0);
	object2->Point3 = CVector(-1000.0,-1000.0,1000.0);
	object2->Material = BrownMatte;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(1000.0,-1000.0,1000.0);
	object2->Point2 = CVector(1000.0,-1000.0,-1000.0);
	object2->Point3 = CVector(-1000.0,-1000.0,1000.0);
	object2->Material = BrownMatte;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,1000.0,-1000.0);
	object2->Point2 = CVector(1000.0,1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,1000.0,1000.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,1000.0,1000.0);
	object2->Point2 = CVector(-1000.0,1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,1000.0,1000.0);
	object2->Material = WhiteGloss;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,1000.0,1000.0);
	object2->Point2 = CVector(-1000.0,-1000.0,1000.0);
	object2->Point3 = CVector(-1000.0,-1000.0,-1000.0);
	object2->Material = RedDarkSatin;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-1000.0,1000.0,-1000.0);
	object2->Point2 = CVector(-1000.0,1000.0,1000.0);
	object2->Point3 = CVector(-1000.0,-1000.0,-1000.0);
	object2->Material = RedDarkSatin;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(1000.0,1000.0,-1000.0);
	object2->Point2 = CVector(1000.0,-1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,-1000.0,1000.0);
	object2->Material = GreenDarkSatin;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(1000.0,1000.0,1000.0);
	object2->Point2 = CVector(1000.0,1000.0,-1000.0);
	object2->Point3 = CVector(1000.0,-1000.0,1000.0);
	object2->Material = GreenDarkSatin;

	Objects.push_back(object2);
*/
/*	object2 = new CTriangleObject;
	object2->Point1 = CVector(-90.0,0.0,0.0);
	object2->Point2 = CVector(-30.0,0.0,0.0);
	object2->Point3 = CVector(-30.0,300.0,0.0);
	object2->Material = BlueShine;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-90.0,300.0,0.0);
	object2->Point2 = CVector(-90.0,0.0,0.0);
	object2->Point3 = CVector(-30.0,300.0,0.0);
	object2->Material = BlueShine;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(30.0,0.0,0.0);
	object2->Point2 = CVector(90.0,0.0,0.0);
	object2->Point3 = CVector(90.0,300.0,0.0);
	object2->Material = GreenMatte;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(30.0,300.0,0.0);
	object2->Point2 = CVector(30.0,0.0,0.0);
	object2->Point3 = CVector(90.0,300.0,0.0);
	object2->Material = GreenMatte;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-150.0,0.0,500.0);
	object2->Point2 = CVector(150.0,0.0,500.0);
	object2->Point3 = CVector(150.0,0.0,-100.0);
	object2->Material = Mirror2;

	Objects.push_back(object2);

	object2 = new CTriangleObject;
	object2->Point1 = CVector(-150.0,0.0,-100.0);
	object2->Point2 = CVector(-150.0,0.0,500.0);
	object2->Point3 = CVector(150.0,0.0,-100.0);
	object2->Material = Mirror2;

	Objects.push_back(object2);
*/	//object2 = new CTriangleObject;
	//object2->Point1 = CVector(-30000.0,-100.0,30000.0);
	//object2->Point2 = CVector(30000.0,-100.0,30000.0);
	//object2->Point3 = CVector(30000.0,-100.0,-30000.0);
	//object2->Material = BlueShine;

	//Objects.push_back(object2);



	MetaGroup *Blob = new MetaGroup;
	
	Blob->Selected=false;
	Blob->Radius=325;
//	Blob->Material=*(it->second);
	Blob->NegativeObject=false;
	Center pos;
	pos.Position=CVector(cos(FrameNumber*2*PI/1000)*3000+sin(FrameNumber*2*PI/200)*300,sin(FrameNumber*2*PI/200)*600,sin(PI/3+FrameNumber*2*PI/200)*150);
	std::map<std::string,CMaterial*>::iterator it = Materials.find("RedShine");
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000)*3000+sin(FrameNumber*2*PI/125)*300,sin(FrameNumber*2*PI/250)*600,sin(PI/3+FrameNumber*2*PI/100)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000)*3000+sin(FrameNumber*2*PI/250)*300,sin(FrameNumber*2*PI/100)*600,sin(PI/3+FrameNumber*2*PI/125)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000)*3000+sin(FrameNumber*2*PI/100)*300,sin(FrameNumber*2*PI/125)*600,sin(PI/3+FrameNumber*2*PI/250)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);

	pos.Position=CVector(cos(FrameNumber*2*PI/1000+PI)*3000+sin(20+FrameNumber*2*PI/50)*300,sin(20+FrameNumber*2*PI/50)*600,sin(20+PI/3+FrameNumber*2*PI/50)*150);
	it = Materials.find("BlueShine");
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000+PI)*3000+sin(20+FrameNumber*2*PI/125)*300,sin(20+FrameNumber*2*PI/250)*600,sin(20+PI/3+FrameNumber*2*PI/100)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000+PI)*3000+sin(20+FrameNumber*2*PI/250)*300,sin(20+FrameNumber*2*PI/100)*600,sin(20+PI/3+FrameNumber*2*PI/125)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	pos.Position=CVector(cos(FrameNumber*2*PI/1000+PI)*3000+sin(20+FrameNumber*2*PI/100)*300,sin(20+FrameNumber*2*PI/125)*600,sin(20+PI/3+FrameNumber*2*PI/250)*150);
	pos.Material=*(it->second);
	Blob->Centers.push_back(pos);
	Objects.push_back(Blob);

	CLight * Light1;


/*	Light1 = new CLight;
	Light1->Intensity = CColor(50.0,50.0,50.0);
	Light1->Position =  CVector(500.0,-800.0,500.0);

	Lights.push_back(Light1);

	Light1 = new CLight;
	Light1->Intensity = CColor(-50.0,-50.0,-50.0);
	Light1->Position =  CVector(-500.0,-800.0,500.0);

	Lights.push_back(Light1);
*/
	
	Light1 = new CLight;
	Light1->Intensity = CColor(2.0,2.0,2.0);
	Light1->Position =  CVector(cos(FrameNumber*2*PI/1000)*30000,sin(FrameNumber*2*PI/1000)*20000.0,cos(FrameNumber*2*PI/1000)*30000.0);

	Lights.push_back(Light1);

	Light1 = new CLight;
	Light1->Intensity = CColor(2.0,2.0,2.0);
	Light1->Position =  CVector(-cos(FrameNumber*2*PI/1000)*30000,sin(FrameNumber*2*PI/1000)*20000.0,cos(FrameNumber*2*PI/1000)*30000.0);

	Lights.push_back(Light1);

	Light1 = new CLight;
	Light1->Intensity = CColor(2.0,2.0,2.0);
	Light1->Position =  CVector(0,sin(FrameNumber*2*PI/1000)*20000.0,-cos(FrameNumber*2*PI/1000)*30000.0);

	Lights.push_back(Light1);


/* 	for(int i=0;i<2;++i)
 		for(int j=0;j<2;++j)
		{
 			Light1 = new CLight;
 			Light1->Intensity = CColor(0.8f,0.8f,0.8f);
 			Light1->Position =  CVector(i*50.0f-25.0f,990,j*50.0f-25.0f);
 
 			Lights.push_back(Light1);
 		}*/
}

double CScene::AO(const CRay &NormalRay,const CRenderableObject *Object)
{
	double totalAO =0.0;
	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		if(Obj == Object)
			continue;
		if(typeid(*Obj)==typeid(CSphereObject))
		{
			CSphereObject * Sphere = dynamic_cast<CSphereObject *>(Obj);
			const double d = NormalRay.Origin.Distance(Sphere->Position);
			const double r = Sphere->Radius;
			totalAO += sqrt(d*d-r*r)/d;
		}
		//else if(typeid(*Obj)==typeid(CTriangleObject))
		//{
		//	CTriangleObject * Triangle = dynamic_cast<CTriangleObject *>(Obj);
		//	const CVector TriangleBarycenter = Triangle->GetBarycenter();
		//	const CVector N1 = NormalRay.Direction;
		//	CVector D = CVector(TriangleBarycenter.x-NormalRay.Origin.x,TriangleBarycenter.y-NormalRay.Origin.y,TriangleBarycenter.z-NormalRay.Origin.z);
		//	D.Normalize();
		//	const double CoefLambert = std::max(0.0,D.DotProduct(N1));

		//	totalAO +=  Triangle->SolidAngleOnSphere(NormalRay.Origin)/(4*PI)/*CoefLambert*/;
		//}
		else if(typeid(*Obj)==typeid(CTriangleObject))
		{
			CTriangleObject * Triangle = dynamic_cast<CTriangleObject *>(Obj);
			CRay TriangleNormal;
			Triangle->GetNormal(Triangle->Point1,TriangleNormal);
			const CVector N = NormalRay.Direction;
			const CVector D = TriangleNormal.Direction;

//			totalAO += cos(std::max(0.0,(acos(D.DotProduct(N))/2)));
		}
		//else if(typeid(*Obj)==typeid(CPlaneObject))
		//{
		//	CPlaneObject * Plane = dynamic_cast<CPlaneObject *>(Obj);
		//	CRay PlaneNormal;
		//	Plane->GetNormal(Plane->Point1,PlaneNormal);
		//	const CVector N = NormalRay.Direction;
		//	const CVector D = PlaneNormal.Direction;

		//	totalAO += cos(std::max(0.0,(acos(D.DotProduct(N)))/2));
		//}
	}

	return totalAO;

}


void CScene::BuildGrid()
{
	m_Grid = new std::vector<CRenderableObject*>[GRIDSIZE*GRIDSIZE*GRIDSIZE];
	CVector p1;
	CVector p2;
	p1 = CVector(-10, -5, -6);
	p2 = CVector( 20, 8, 30 );
	double dx = (p2.x - p1.x) / GRIDSIZE;
	double dy = (p2.y - p1.y) / GRIDSIZE;
	double dz = (p2.z - p1.z) / GRIDSIZE;
	CVector halfsize = (p2 - p1) * (0.5f / GRIDSIZE);
	for ( int x = 0; x < GRIDSIZE; x++ ) for ( int y = 0; y < GRIDSIZE; y++ ) for ( int z = 0; z < GRIDSIZE; z++ )
	{
		int idx = x + y * GRIDSIZE + z * GRIDSIZE * GRIDSIZE;
		CVector pos( p1.x + x * dx, p1.y + y * dy, p1.z + z * dz );
		for ( std::vector<CRenderableObject*>::iterator it = Objects.begin();it != Objects.end();++it)
		{
			if ((*it)->OverlapBox( pos, halfsize ))
			{
				m_Grid[idx].push_back(*it);
			}
		}
		idx++;
	}
}



void CScene::LoadMaterials(std::string FileName)
{
	std::ifstream ifichier(FileName.c_str());

	unsigned int NbrMaterials;
	ifichier >> NbrMaterials;

	for(unsigned int i=0;i<NbrMaterials;++i)
	{
		CMaterial* Material=new CMaterial;
		std::string name;
		ifichier >> name;
		ifichier >> Material->color.r;
		ifichier >> Material->color.g;
		ifichier >> Material->color.b;
		ifichier >> Material->diffuse;
		ifichier >> Material->specular;
		ifichier >> Material->specularExponent;
		ifichier >> Material->reflection.r;
		ifichier >> Material->reflection.g;
		ifichier >> Material->reflection.b;
		ifichier >> Material->reflectionScatter;
		ifichier >> Material->transparency;
		ifichier >> Material->refractionIndex.r;
		ifichier >> Material->refractionIndex.g;
		ifichier >> Material->refractionIndex.b;
		Materials[name] = Material;
	}

}

void CScene::LoadObjects(std::string FileName,CCamera * camera)
{
	std::ifstream ifichier(FileName.c_str());

	unsigned int NbrObjects;
	ifichier >> NbrObjects;

	ifichier >> camera->LookAt.x;
	ifichier >> camera->LookAt.y;
	ifichier >> camera->LookAt.z;

	ifichier >> camera->Position.x;
	ifichier >> camera->Position.y;
	ifichier >> camera->Position.z;

	double fov;
	ifichier >> fov;
	camera->SetFOV(fov);

	for(unsigned int i=0;i<NbrObjects;++i)
	{
		std::string Type;
		ifichier >> Type;
		if (Type == "SPHERE")
		{
			int Negative;
			ifichier >> Negative;
			std::string MaterialName;
			ifichier >> MaterialName;
			std::map<std::string,CMaterial*>::iterator it = Materials.find(MaterialName);
			if(it==Materials.end())
				__debugbreak();
			CSphereObject* Object1=new CSphereObject;
			Object1->Selected=false;
			Object1->Material=*(it->second);
			Object1->NegativeObject=(Negative==1);
			ifichier >> Object1->Position.x;
			ifichier >> Object1->Position.y;
			ifichier >> Object1->Position.z;
			ifichier >> Object1->Radius;
			Objects.push_back(Object1);
		}
		else if (Type == "METAOBJECT")
		{
			int Negative;
			ifichier >> Negative;
			std::string MaterialName;
			ifichier >> MaterialName;
			std::map<std::string,CMaterial*>::iterator it = Materials.find(MaterialName);
			if(it==Materials.end())
				__debugbreak();
			int NB_Emitters;
			ifichier >> NB_Emitters;
			MetaGroup *Object1 =new MetaGroup;
			ifichier >> Object1->Radius;
			Object1->Selected=false;
			Object1->NegativeObject=(Negative==1);
			for(int j=0;j<NB_Emitters;++j)
			{
				Center Pos;
				Pos.Material=*(it->second);
				ifichier >> Pos.Position.x;
				ifichier >> Pos.Position.y;
				ifichier >> Pos.Position.z;
				Object1->Centers.push_back(Pos);
			}
			Objects.push_back(Object1);
		}
		else if (Type == "TRIANGLE")
		{
			int Negative;
			ifichier >> Negative;
			std::string MaterialName;
			ifichier >> MaterialName;
			std::map<std::string,CMaterial*>::iterator it = Materials.find(MaterialName);
			if(it==Materials.end())
				__debugbreak();
			CTriangleObject* Object1=new CTriangleObject;
			Object1->Selected=false;
			Object1->Material=*(it->second);
			Object1->NegativeObject=(Negative==1);
			ifichier >> Object1->Point1.x;
			ifichier >> Object1->Point1.y;
			ifichier >> Object1->Point1.z;
			ifichier >> Object1->Point2.x;
			ifichier >> Object1->Point2.y;
			ifichier >> Object1->Point2.z;
			ifichier >> Object1->Point3.x;
			ifichier >> Object1->Point3.y;
			ifichier >> Object1->Point3.z;
			Objects.push_back(Object1);
		}
		else if (Type == "PLIGHT")
		{
			CLight* Light1 = new CLight;
			Light1->Type = CLight::LightType_Point;
			ifichier >> Light1->Intensity.r;
			ifichier >> Light1->Intensity.g;
			ifichier >> Light1->Intensity.b;
			ifichier >> Light1->Position.x;
			ifichier >> Light1->Position.y;
			ifichier >> Light1->Position.z;
			Lights.push_back(Light1);
		}
		else if (Type == "SLIGHT")
		{
			CLight* Light1 = new CLight;
			Light1->Type = CLight::LightType_Spot;
			ifichier >> Light1->Intensity.r;
			ifichier >> Light1->Intensity.g;
			ifichier >> Light1->Intensity.b;
			ifichier >> Light1->Position.x;
			ifichier >> Light1->Position.y;
			ifichier >> Light1->Position.z;
			ifichier >> Light1->Direction.x;
			ifichier >> Light1->Direction.y;
			ifichier >> Light1->Direction.z;
			Light1->Direction=Light1->Direction-Light1->Position;
			Light1->Direction.Normalize();
			ifichier >> Light1->ConeAngle;
			Lights.push_back(Light1);
		}
	}
}

CRenderableObject * CScene::Scene_GetObject(const CRay &Ray)
{
	double MinimumDistance = Infinity;
	CRenderableObject* ClosestObject = NULL;

	for(std::vector<CRenderableObject*>::iterator it = Objects.begin(); it != Objects.end(); ++it)
	{
		CRenderableObject* Obj = *it;
		double Distance = Obj->HitDistance(Ray);
		if (Distance>eps && Distance < MinimumDistance)
		{
			MinimumDistance = Distance;
			ClosestObject = Obj;
		}
	}
	return ClosestObject;
}
